package juc.executors;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

public class ExecutorServiceExample1 {
    /**
     * The demo for class {@link java.util.concurrent.ExecutorService}
     */
    public static void main(String[] args) throws InterruptedException {
        //isShutDown();
        //isTerminated();
        //executeRunnableError();
        executeRunnableTask();
    }

    /**
     * {@link ExecutorService#isShutdown()}
     * Question:
     * when invoked the shutdown method,can execute the new runnable?
     * Answer:NO, the Executor Service will rejected after shutdown,throw RejectedExecutionException
     */
    private static void isShutDown() {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(() -> {
            timeSleep(4);
        });
        System.out.println(executorService.isShutdown());
        executorService.shutdown();
        System.out.println(executorService.isShutdown());
        executorService.execute(() -> {
            System.out.println("i will be executed after shutdown ?");
        });
    }

    /**
     * {@link ExecutorService#isTerminated()}
     * {@link ThreadPoolExecutor#isTerminating()}
     */
    private static void isTerminated() {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.execute(() -> {
            timeSleep(2);
        });
        executorService.shutdown();
        System.out.println(executorService.isShutdown());
        //还有活动的线程不会终结
        System.out.println(executorService.isTerminated());
        System.out.println(((ThreadPoolExecutor) executorService).isTerminating());

    }

    private static void executeRunnableError() throws InterruptedException {
        ExecutorService executorService = Executors
                .newFixedThreadPool(10, new MyThreadFactory());

        IntStream.range(0, 10).boxed().forEach(i -> executorService.execute(
                () -> System.out.println(1 / 0)));
        executorService.shutdown();
        executorService.awaitTermination(10, TimeUnit.MINUTES);
        System.out.println("===");
    }

    /**
     * 把任务包住捕获所有的异常，不交给线程池处理异常
     */
    private static void executeRunnableTask() throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        IntStream.range(0, 10).boxed().forEach(i -> executorService.execute(
                new MyTask(i) {
                    @Override
                    protected void error(Throwable cause) {
                        System.out.println("the no:" + i + " failed,update status to error");
                    }

                    @Override
                    protected void done() {
                        System.out.println("the no:" + i + " successfully,update status to done");
                    }

                    @Override
                    protected void doExecute() {
                        if (i % 3 == 0) {
                            int tmp = i / 0;
                        }
                    }

                    @Override
                    protected void doInit() {
                        //do nothing
                    }
                }
        ));
        executorService.shutdown();
        executorService.awaitTermination(10, TimeUnit.MINUTES);
        System.out.println("===");
    }

    private abstract static class MyTask implements Runnable {
        protected final int no;

        private MyTask(int no) {
            this.no = no;
        }

        @Override
        public void run() {
            try {
                this.doInit();
                this.doExecute();
                this.done();
            } catch (Throwable cause) {
                this.error(cause);
            }
        }

        protected abstract void error(Throwable cause);

        protected abstract void done();

        protected abstract void doExecute();

        protected abstract void doInit();
    }

    //通过setUncaughtExceptionHandler()获取任务中的RuntimeException异常
    //如果任务比较复杂，不建议用这种方式，因为uncaughtException拿到的只有Thread和Throwable
    private static class MyThreadFactory implements ThreadFactory {
        private final static AtomicInteger seq = new AtomicInteger();

        @Override
        public Thread newThread(Runnable r) {
            //调用的是这里的run，这样能感知线程出错
            Thread thread = new Thread(r);
            thread.setName("My-Thread-" + seq.getAndIncrement());
            //当线程出问题时会回调UncaughtExceptionHandler#uncaughtException(Thread t, Throwable e)
            thread.setUncaughtExceptionHandler((t, cause) -> {
                System.out.println("the thread " + t.getName() + " execute failed");
                cause.printStackTrace();
                System.out.println("======");
            });
            return thread;
        }
    }


    private static void timeSleep(long time) {
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
